/*
 *underscore函数扩充
 */
var path = require('path'),
    fs = require('fs'),
    _ = require('underscore');

var mixin = {
    get_group_name: function() {
        return _.empty(process.env.group_name) ? 'seller' : process.env.group_name;
    },
    addcslashes: function(str, charlist) {
        var target = '',
            chrs = [],
            i = 0,
            j = 0,
            c = '',
            next = '',
            rangeBegin = '',
            rangeEnd = '',
            chr = '',
            begin = 0,
            end = 0,
            octalLength = 0,
            postOctalPos = 0,
            cca = 0,
            escHexGrp = [],
            encoded = '',
            percentHex = /%([\dA-Fa-f]+)/g;
        var _pad = function(n, c) {
            if ((n = n + '').length < c) {
                return new Array(++c - n.length).join('0') + n;
            }
            return n;
        };

        for (i = 0; i < charlist.length; i++) {
            c = charlist.charAt(i);
            next = charlist.charAt(i + 1);
            if (c === '\\' && next && (/\d/).test(next)) { // Octal
                rangeBegin = charlist.slice(i + 1).match(/^\d+/)[0];
                octalLength = rangeBegin.length;
                postOctalPos = i + octalLength + 1;
                if (charlist.charAt(postOctalPos) + charlist.charAt(postOctalPos + 1) === '..') { // Octal begins range
                    begin = rangeBegin.charCodeAt(0);
                    if ((/\\\d/).test(charlist.charAt(postOctalPos + 2) + charlist.charAt(postOctalPos + 3))) { // Range ends with octal
                        rangeEnd = charlist.slice(postOctalPos + 3).match(/^\d+/)[0];
                        i += 1; // Skip range end backslash
                    } else if (charlist.charAt(postOctalPos + 2)) { // Range ends with character
                        rangeEnd = charlist.charAt(postOctalPos + 2);
                    } else {
                        throw 'Range with no end point';
                    }
                    end = rangeEnd.charCodeAt(0);
                    if (end > begin) { // Treat as a range
                        for (j = begin; j <= end; j++) {
                            chrs.push(String.fromCharCode(j));
                        }
                    } else { // Supposed to treat period, begin and end as individual characters only, not a range
                        chrs.push('.', rangeBegin, rangeEnd);
                    }
                    i += rangeEnd.length + 2; // Skip dots and range end (already skipped range end backslash if present)
                } else { // Octal is by itself
                    chr = String.fromCharCode(parseInt(rangeBegin, 8));
                    chrs.push(chr);
                }
                i += octalLength; // Skip range begin
            } else if (next + charlist.charAt(i + 2) === '..') { // Character begins range
                rangeBegin = c;
                begin = rangeBegin.charCodeAt(0);
                if ((/\\\d/).test(charlist.charAt(i + 3) + charlist.charAt(i + 4))) { // Range ends with octal
                    rangeEnd = charlist.slice(i + 4).match(/^\d+/)[0];
                    i += 1; // Skip range end backslash
                } else if (charlist.charAt(i + 3)) { // Range ends with character
                    rangeEnd = charlist.charAt(i + 3);
                } else {
                    throw 'Range with no end point';
                }
                end = rangeEnd.charCodeAt(0);
                if (end > begin) { // Treat as a range
                    for (j = begin; j <= end; j++) {
                        chrs.push(String.fromCharCode(j));
                    }
                } else { // Supposed to treat period, begin and end as individual characters only, not a range
                    chrs.push('.', rangeBegin, rangeEnd);
                }
                i += rangeEnd.length + 2; // Skip dots and range end (already skipped range end backslash if present)
            } else { // Character is by itself
                chrs.push(c);
            }
        }

        for (i = 0; i < str.length; i++) {
            c = str.charAt(i);
            if (chrs.indexOf(c) !== -1) {
                target += '\\';
                cca = c.charCodeAt(0);
                if (cca < 32 || cca > 126) { // Needs special escaping
                    switch (c) {
                        case '\n':
                            target += 'n';
                            break;
                        case '\t':
                            target += 't';
                            break;
                        case '\u000D':
                            target += 'r';
                            break;
                        case '\u0007':
                            target += 'a';
                            break;
                        case '\v':
                            target += 'v';
                            break;
                        case '\b':
                            target += 'b';
                            break;
                        case '\f':
                            target += 'f';
                            break;
                        default:
                            //target += _pad(cca.toString(8), 3);break; // Sufficient for UTF-16
                            encoded = encodeURIComponent(c);

                            // 3-length-padded UTF-8 octets
                            if ((escHexGrp = percentHex.exec(encoded)) !== null) {
                                target += _pad(parseInt(escHexGrp[1], 16).toString(8), 3); // already added a slash above
                            }
                            while ((escHexGrp = percentHex.exec(encoded)) !== null) {
                                target += '\\' + _pad(parseInt(escHexGrp[1], 16).toString(8), 3);
                            }
                            break;
                    }
                } else { // Perform regular backslashed escaping
                    target += c;
                }
            } else { // Just add the character unescaped
                target += c;
            }
        }
        return target;
    },

    sprintf: function() {
        var regex = /%%|%(\d+\$)?([-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuideEfFgG])/g;
        var a = arguments,
            i = 0,
            format = a[i++];

        // pad()
        var pad = function(str, len, chr, leftJustify) {
            if (!chr) {
                chr = ' ';
            }
            var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
            return leftJustify ? str + padding : padding + str;
        };

        // justify()
        var justify = function(value, prefix, leftJustify, minWidth, zeroPad, customPadChar) {
            var diff = minWidth - value.length;
            if (diff > 0) {
                if (leftJustify || !zeroPad) {
                    value = pad(value, minWidth, customPadChar, leftJustify);
                } else {
                    value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
                }
            }
            return value;
        };

        // formatBaseX()
        var formatBaseX = function(value, base, prefix, leftJustify, minWidth, precision, zeroPad) {
            // Note: casts negative numbers to positive ones
            var number = value >>> 0;
            prefix = prefix && number && {
                '2': '0b',
                '8': '0',
                '16': '0x'
            }[base] || '';
            value = prefix + pad(number.toString(base), precision || 0, '0', false);
            return justify(value, prefix, leftJustify, minWidth, zeroPad);
        };

        // formatString()
        var formatString = function(value, leftJustify, minWidth, precision, zeroPad, customPadChar) {
            if (precision != null) {
                value = value.slice(0, precision);
            }
            return justify(value, '', leftJustify, minWidth, zeroPad, customPadChar);
        };

        // doFormat()
        var doFormat = function(substring, valueIndex, flags, minWidth, _, precision, type) {
            var number;
            var prefix;
            var method;
            var textTransform;
            var value;

            if (substring === '%%') {
                return '%';
            }

            // parse flags
            var leftJustify = false,
                positivePrefix = '',
                zeroPad = false,
                prefixBaseX = false,
                customPadChar = ' ';
            var flagsl = flags.length;
            for (var j = 0; flags && j < flagsl; j++) {
                switch (flags.charAt(j)) {
                    case ' ':
                        positivePrefix = ' ';
                        break;
                    case '+':
                        positivePrefix = '+';
                        break;
                    case '-':
                        leftJustify = true;
                        break;
                    case "'":
                        customPadChar = flags.charAt(j + 1);
                        break;
                    case '0':
                        zeroPad = true;
                        break;
                    case '#':
                        prefixBaseX = true;
                        break;
                }
            }

            // parameters may be null, undefined, empty-string or real valued
            // we want to ignore null, undefined and empty-string values
            if (!minWidth) {
                minWidth = 0;
            } else if (minWidth === '*') {
                minWidth = +a[i++];
            } else if (minWidth.charAt(0) == '*') {
                minWidth = +a[minWidth.slice(1, -1)];
            } else {
                minWidth = +minWidth;
            }

            // Note: undocumented perl feature:
            if (minWidth < 0) {
                minWidth = -minWidth;
                leftJustify = true;
            }

            if (!isFinite(minWidth)) {
                throw new Error('sprintf: (minimum-)width must be finite');
            }

            if (!precision) {
                precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type === 'd') ? 0 : undefined;
            } else if (precision === '*') {
                precision = +a[i++];
            } else if (precision.charAt(0) == '*') {
                precision = +a[precision.slice(1, -1)];
            } else {
                precision = +precision;
            }

            // grab value using valueIndex if required?
            value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];

            switch (type) {
                case 's':
                    return formatString(String(value), leftJustify, minWidth, precision, zeroPad, customPadChar);
                case 'c':
                    return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad);
                case 'b':
                    return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
                case 'o':
                    return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
                case 'x':
                    return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
                case 'X':
                    return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase();
                case 'u':
                    return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
                case 'i':
                case 'd':
                    number = +value || 0;
                    number = Math.round(number - number % 1); // Plain Math.round doesn't just truncate
                    prefix = number < 0 ? '-' : positivePrefix;
                    value = prefix + pad(String(Math.abs(number)), precision, '0', false);
                    return justify(value, prefix, leftJustify, minWidth, zeroPad);
                case 'e':
                case 'E':
                case 'f': // Should handle locales (as per setlocale)
                case 'F':
                case 'g':
                case 'G':
                    number = +value;
                    prefix = number < 0 ? '-' : positivePrefix;
                    method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
                    textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
                    value = prefix + Math.abs(number)[method](precision);
                    return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform]();
                default:
                    return substring;
            }
        };

        return format.replace(regex, doFormat);
    },
    getAllFiles: function(root, recursive) {
        var root_path = null;
        _getAllFiles = function(root, recursive) {
            var res = [],
                files = fs.readdirSync(root);
            if (root_path == null) {
                root_path = root;
            }
            files.forEach(function(file) {
                var pathname = root + '/' + file,
                    stat = fs.lstatSync(pathname);

                if (!stat.isDirectory()) {
                    res.push(pathname);
                } else {
                    if (recursive) {
                        res = res.concat(_getAllFiles(pathname));
                    }
                }
            });
            return res
        }
        return _getAllFiles(root, recursive);
    },
    getFilesName: function(root) {
        var result = [],
            files = fs.readdirSync(root);
        files.forEach(function(file) {
            if (!fs.lstatSync(root + '/' + file).isDirectory()) {
                result.push(file);
            }
        });
        return result
    },
    getDirName: function(root) {
        var result = [],
            files = fs.readdirSync(root);

        files.forEach(function(file) {
            if (fs.lstatSync(root + '/' + file).isDirectory()) {
                result.push(file);
            }
        });
        return result;
    },
    copy: function(src, dst, log) {
        var is = fs.createReadStream(src);
        var os = fs.createWriteStream(dst);
        if (log) {
            console.log("copy:" + dst);
        }
        return is.pipe(os);
    },
    copyRecursive: function(src, dist, pattern) {
        src = _.trim(src);
        dist = _.trim(dist);
        if (src.lastIndexOf('/') != src.length - 1 && src.lastIndexOf('\\') != src.length - 1) src += "/";
        if (dist.lastIndexOf('/') != dist.length - 1 && dist.lastIndexOf('\\') != dist.length - 1) dist += "/";

        var src_root = src,
            dist_root = dist;
        _copyRecursive = function(src, dist) {
            var files = fs.readdirSync(src);
            files.forEach(function(file) {
                var src_path = src + file,
                    dist_path = dist + file,
                    stat = fs.lstatSync(src_path);
                if (!fs.existsSync(dist)) {
                    fs.mkdirSync(dist);
                }
                if (!stat.isDirectory()) {
                    if (pattern) {
                        var ext = _.getExtension(src_path);
                        if (!_.in_array(ext, pattern)) return;
                    }
                    _.copy(src_path, dist_path);
                } else {
                    _copyRecursive(src_path + '/', dist_path + '/');
                }
            });
        }
        return _copyRecursive(src, dist);
    },
    getExtension: function(path) {
        return _.trim(path.substr(path.lastIndexOf('.') + 1));
    },
    jsonPath: function(obj, expr, arg) {
        var P = {
            resultType: arg && arg.resultType || "VALUE",
            result: [],
            normalize: function(expr) {
                var subx = [];
                return expr.replace(/[\['](\??\(.*?\))[\]']/g, function($0, $1) {
                        return "[#" + (subx.push($1) - 1) + "]";
                    })
                    .replace(/'?\.'?|\['?/g, ";")
                    .replace(/;;;|;;/g, ";..;")
                    .replace(/;$|'?\]|'$/g, "")
                    .replace(/#([0-9]+)/g, function($0, $1) {
                        return subx[$1];
                    });
            },
            asPath: function(path) {
                var x = path.split(";"),
                    p = "";
                for (var i = 1, n = x.length; i < n; i++)
                    p += /^[0-9*]+$/.test(x[i]) ? ("[" + x[i] + "]") : ("['" + x[i] + "']");
                return p;
            },
            store: function(p, v) {
                if (p) P.result[P.result.length] = P.resultType == "PATH" ? P.asPath(p) : v;
                return !!p;
            },
            trace: function(expr, val, path) {
                if (expr) {
                    var x = expr.split(";"),
                        loc = x.shift();
                    x = x.join(";");
                    if (val && val.hasOwnProperty(loc))
                        P.trace(x, val[loc], path + ";" + loc);
                    else if (loc === "*")
                        P.walk(loc, x, val, path, function(m, l, x, v, p) {
                            P.trace(m + ";" + x, v, p);
                        });
                    else if (loc === "..") {
                        P.trace(x, val, path);
                        P.walk(loc, x, val, path, function(m, l, x, v, p) {
                            typeof v[m] === "object" && P.trace("..;" + x, v[m], p + ";" + m);
                        });
                    } else if (/,/.test(loc)) { // [name1,name2,...]
                        for (var s = loc.split(/'?,'?/), i = 0, n = s.length; i < n; i++)
                            P.trace(s[i] + ";" + x, val, path);
                    } else if (/^\(.*?\)$/.test(loc)) // [(expr)]
                        P.trace(P.eval(loc, val, path.substr(path.lastIndexOf(";") + 1)) + ";" + x, val, path);
                    else if (/^\?\(.*?\)$/.test(loc)) // [?(expr)]
                        P.walk(loc, x, val, path, function(m, l, x, v, p) {
                        if (P.eval(l.replace(/^\?\((.*?)\)$/, "$1"), v[m], m)) P.trace(m + ";" + x, v, p);
                    });
                    else if (/^(-?[0-9]*):(-?[0-9]*):?([0-9]*)$/.test(loc)) // [start:end:step]  phyton slice syntax
                        P.slice(loc, x, val, path);
                } else
                    P.store(path, val);
            },
            walk: function(loc, expr, val, path, f) {
                if (val instanceof Array) {
                    for (var i = 0, n = val.length; i < n; i++)
                        if (i in val)
                            f(i, loc, expr, val, path);
                } else if (typeof val === "object") {
                    for (var m in val)
                        if (val.hasOwnProperty(m))
                            f(m, loc, expr, val, path);
                }
            },
            slice: function(loc, expr, val, path) {
                if (val instanceof Array) {
                    var len = val.length,
                        start = 0,
                        end = len,
                        step = 1;
                    loc.replace(/^(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)$/g, function($0, $1, $2, $3) {
                        start = parseInt($1 || start);
                        end = parseInt($2 || end);
                        step = parseInt($3 || step);
                    });
                    start = (start < 0) ? Math.max(0, start + len) : Math.min(len, start);
                    end = (end < 0) ? Math.max(0, end + len) : Math.min(len, end);
                    for (var i = start; i < end; i += step)
                        P.trace(i + ";" + expr, val, path);
                }
            },
            eval: function(x, _v, _vname) {
                try {
                    return $ && _v && eval(x.replace(/@/g, "_v"));
                } catch (e) {
                    throw new SyntaxError("jsonPath: " + e.message + ": " + x.replace(/@/g, "_v").replace(/\^/g, "_a"));
                }
            }
        };

        var $ = obj;
        if (expr && obj && (P.resultType == "VALUE" || P.resultType == "PATH")) {
            P.trace(P.normalize(expr).replace(/^\$;/, ""), obj, "$");
            return P.result.length ? P.result : false;
        }
    },
    rsort: function(inputArr, sort_flags) {
        var valArr = [],
            k = '',
            i = 0,
            sorter = false,
            that = this,
            strictForIn = false,
            populateArr = [];

        switch (sort_flags) {
            case 'SORT_STRING':
                // compare items as strings
                sorter = function(a, b) {
                    return that.strnatcmp(b, a);
                };
                break;
            case 'SORT_LOCALE_STRING':
                // compare items as strings, based on the current locale (set with  i18n_loc_set_default() as of PHP6)
                var loc = this.i18n_loc_get_default();
                sorter = this.php_js.i18nLocales[loc].sorting;
                break;
            case 'SORT_NUMERIC':
                // compare items numerically
                sorter = function(a, b) {
                    return (b - a);
                };
                break;
            case 'SORT_REGULAR':
                // compare items normally (don't change types)
            default:
                sorter = function(b, a) {
                    var aFloat = parseFloat(a),
                        bFloat = parseFloat(b),
                        aNumeric = aFloat + '' === a,
                        bNumeric = bFloat + '' === b;
                    if (aNumeric && bNumeric) {
                        return aFloat > bFloat ? 1 : aFloat < bFloat ? -1 : 0;
                    } else if (aNumeric && !bNumeric) {
                        return 1;
                    } else if (!aNumeric && bNumeric) {
                        return -1;
                    }
                    return a > b ? 1 : a < b ? -1 : 0;
                };
                break;
        }

        // BEGIN REDUNDANT
        this.php_js = this.php_js || {};
        this.php_js.ini = this.php_js.ini || {};
        // END REDUNDANT
        strictForIn = this.php_js.ini['phpjs.strictForIn'] && this.php_js.ini['phpjs.strictForIn'].local_value && this.php_js.ini['phpjs.strictForIn'].local_value !== 'off';
        populateArr = strictForIn ? inputArr : populateArr;

        for (k in inputArr) { // Get key and value arrays
            if (inputArr.hasOwnProperty(k)) {
                valArr.push(inputArr[k]);
                if (strictForIn) {
                    delete inputArr[k];
                }
            }
        }

        valArr.sort(sorter);

        for (i = 0; i < valArr.length; i++) { // Repopulate the old array
            populateArr[i] = valArr[i];
        }
        return strictForIn || populateArr;
    },
    sort: function(inputArr, sort_flags) {
        var valArr = [],
            keyArr = [],
            k = '',
            i = 0,
            sorter = false,
            that = this,
            strictForIn = false,
            populateArr = [];

        switch (sort_flags) {
            case 'SORT_STRING':
                // compare items as strings
                sorter = function(a, b) {
                    return that.strnatcmp(a, b);
                };
                break;
            case 'SORT_LOCALE_STRING':
                // compare items as strings, based on the current locale (set with  i18n_loc_set_default() as of PHP6)
                var loc = this.i18n_loc_get_default();
                sorter = this.php_js.i18nLocales[loc].sorting;
                break;
            case 'SORT_NUMERIC':
                // compare items numerically
                sorter = function(a, b) {
                    return (a - b);
                };
                break;
            case 'SORT_REGULAR':
                // compare items normally (don't change types)
            default:
                sorter = function(a, b) {
                    var aFloat = parseFloat(a),
                        bFloat = parseFloat(b),
                        aNumeric = aFloat + '' === a,
                        bNumeric = bFloat + '' === b;
                    if (aNumeric && bNumeric) {
                        return aFloat > bFloat ? 1 : aFloat < bFloat ? -1 : 0;
                    } else if (aNumeric && !bNumeric) {
                        return 1;
                    } else if (!aNumeric && bNumeric) {
                        return -1;
                    }
                    return a > b ? 1 : a < b ? -1 : 0;
                };
                break;
        }

        // BEGIN REDUNDANT
        this.php_js = this.php_js || {};
        this.php_js.ini = this.php_js.ini || {};
        // END REDUNDANT
        strictForIn = this.php_js.ini['phpjs.strictForIn'] && this.php_js.ini['phpjs.strictForIn'].local_value && this.php_js.ini['phpjs.strictForIn'].local_value !== 'off';
        populateArr = strictForIn ? inputArr : populateArr;

        for (k in inputArr) { // Get key and value arrays
            if (inputArr.hasOwnProperty(k)) {
                valArr.push(inputArr[k]);
                if (strictForIn) {
                    delete inputArr[k];
                }
            }
        }

        valArr.sort(sorter);

        for (i = 0; i < valArr.length; i++) { // Repopulate the old array
            populateArr[i] = valArr[i];
        }
        return strictForIn || populateArr;
    },
    insert: function(path, data, list, field, order) {
        var _path = _.jsonPath(list, path, {
                resultType: "PATH"
            }),
            data = JSON.stringify(data);
        //console.log(_path,path,data,list);
        _.each(_path, function(val, key, _list) {
            var str = 'if(_.isArray(list' + val + ')){\
                    list' + val + '.push(' + data + ');\
                }else{\
                    list' + val + '=' + data + ';\
                }';

            eval(str);
        });
        if (field) {
            _.asort(path, field, order, list);
        }
        return list;
    },
    asort: function(path, field, order, list) {
        var _path = _.jsonPath(list, path, {
                resultType: "PATH"
            }),
            data = _.jsonPath(list, path);

        _.each(_path, function(val, key) {
            var res = {},
                fieldVal = [],
                result = [];
            _.each(data[key], function(v) {
                res[v[field]] = v;
                fieldVal.push(v[field]);
            });
            if (order == 'asc') {
                fieldVal = _.sort(fieldVal, 'SORT_NUMERIC');
            } else {
                fieldVal = _.rsort(fieldVal, 'SORT_NUMERIC');
            }
            _.each(fieldVal, function(val) {
                result.push(res[val]);
            });
            eval('list' + val + '=' + JSON.stringify(result));
        });
        return list;
    },
    delete: function(path, list) {
        var _path = _.jsonPath(list, path, {
                resultType: "PATH"
            }),
            data = _.jsonPath(list, path),
            result = [];
        _.each(_path, function(val, key) {
            var res = val.split('[');
            var index = parseInt(res[res.length - 1].substr(0, res[res.length - 1].length - 1));
            eval('list' + val.substr(0, val.lastIndexOf('[')) + '.splice(' + index + ',1)');
        });
        return list;
    },
    update: function(path, data, list) {
        var _path = _.jsonPath(list, path, {
                resultType: "PATH"
            }),
            data = JSON.stringify(data);
        _.each(_path, function(val, key) {
            eval('list' + val + '=' + data);
        });
        return list;
    },
    get: function(path, list) {
        var data = _.jsonPath(list, path);
        return data[0];
    },
    getThumb: function(path, full) {
        var result = "";
        if (path) {
            var pos = path.lastIndexOf('.');
            result = path.substring(0, pos) + '_thumb' + path.substring(pos);
            if (full) {
                result = _global.upload_url + result;
            }
        }
        return result;
    },
    getOrigImg: function(path, full) {
        var result = "";
        if (path) {
            var pos = path.lastIndexOf('_thumb');
            result = path.substring(0, pos) + path.substring(pos + '_thumb'.length);
            if (full) {
                result = _global.upload_url + result;
            }
        }
        return result;
    },
    rand: function(min, max) {
        var argc = arguments.length;
        if (argc === 0) {
            min = 0;
            max = 2147483647;
        } else if (argc === 1) {
            throw new Error('Warning: rand() expects exactly 2 parameters, 1 given');
        }
        return Math.floor(Math.random() * (max - min + 1)) + min;
    },
    parse_url: function(str, component) {
        var query, key = ['source', 'scheme', 'authority', 'userInfo', 'user', 'pass', 'host', 'port',
                'relative', 'path', 'directory', 'file', 'query', 'fragment'
            ],
            ini = (this.php_js && this.php_js.ini) || {},
            mode = (ini['phpjs.parse_url.mode'] &&
                ini['phpjs.parse_url.mode'].local_value) || 'php',
            parser = {
                php: /^(?:([^:\/?#]+):)?(?:\/\/()(?:(?:()(?:([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?()(?:(()(?:(?:[^?#\/]*\/)*)()(?:[^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
                strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
                loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/\/?)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // Added one optional slash to post-scheme to catch file:/// (should restrict this)
            };

        var m = parser[mode].exec(str),
            uri = {},
            i = 14;
        while (i--) {
            if (m[i]) {
                uri[key[i]] = m[i];
            }
        }

        if (component) {
            return uri[component.replace('PHP_URL_', '').toLowerCase()];
        }
        if (mode !== 'php') {
            var name = (ini['phpjs.parse_url.queryKey'] &&
                ini['phpjs.parse_url.queryKey'].local_value) || 'queryKey';
            parser = /(?:^|&)([^&=]*)=?([^&]*)/g;
            uri[name] = {};
            query = uri[key[12]] || '';
            query.replace(parser, function($0, $1, $2) {
                if ($1) {
                    uri[name][$1] = $2;
                }
            });
        }
        delete uri.source;
        return uri;
    },
    //去掉首尾空字符
    trim: function(str, charlist) {
        var whitespace, l = 0,
            i = 0;
        str += '';

        if (!charlist) {
            // default list
            whitespace = " \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000";
        } else {
            // preg_quote custom list
            charlist += '';
            whitespace = charlist.replace(/([\[\]\(\)\.\?\/\*\{\}\+\$\^\:])/g, '$1');
        }

        l = str.length;
        for (i = 0; i < l; i++) {
            if (whitespace.indexOf(str.charAt(i)) === -1) {
                str = str.substring(i);
                break;
            }
        }

        l = str.length;
        for (i = l - 1; i >= 0; i--) {
            if (whitespace.indexOf(str.charAt(i)) === -1) {
                str = str.substring(0, i + 1);
                break;
            }
        }
        return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
    },
    str_replace: function(search, replace, subject, count) {
        var i = 0,
            j = 0,
            temp = '',
            repl = '',
            sl = 0,
            fl = 0,
            f = [].concat(search),
            r = [].concat(replace),
            s = subject,
            ra = Object.prototype.toString.call(r) === '[object Array]',
            sa = Object.prototype.toString.call(s) === '[object Array]';
        s = [].concat(s);
        if (count) {
            this.window[count] = 0;
        }

        for (i = 0, sl = s.length; i < sl; i++) {
            if (s[i] === '') {
                continue;
            }
            for (j = 0, fl = f.length; j < fl; j++) {
                temp = s[i] + '';
                repl = ra ? (r[j] !== undefined ? r[j] : '') : r[0];
                s[i] = (temp).split(f[j]).join(repl);
                if (count && s[i] !== temp) {
                    this.window[count] += (temp.length - s[i].length) / f[j].length;
                }
            }
        }
        return sa ? s : s[0];
    },
    date: function(format, timestamp) {
        var that = this,
            jsdate,
            f,
            txt_words = ["Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
            formatChr = /\\?(.?)/gi,
            formatChrCb = function(t, s) {
                return f[t] ? f[t]() : s;
            },
            _pad = function(n, c) {
                n = String(n);
                while (n.length < c) {
                    n = '0' + n;
                }
                return n;
            };
        f = {
            // Day
            d: function() { // Day of month w/leading 0; 01..31
                return _pad(f.j(), 2);
            },
            D: function() { // Shorthand day name; Mon...Sun
                return f.l().slice(0, 3);
            },
            j: function() { // Day of month; 1..31
                return jsdate.getDate();
            },
            l: function() { // Full day name; Monday...Sunday
                return txt_words[f.w()] + 'day';
            },
            N: function() { // ISO-8601 day of week; 1[Mon]..7[Sun]
                return f.w() || 7;
            },
            S: function() { // Ordinal suffix for day of month; st, nd, rd, th
                var j = f.j(),
                    i = j % 10;
                if (i <= 3 && parseInt((j % 100) / 10, 10) == 1) {
                    i = 0;
                }
                return ['st', 'nd', 'rd'][i - 1] || 'th';
            },
            w: function() { // Day of week; 0[Sun]..6[Sat]
                return jsdate.getDay();
            },
            z: function() { // Day of year; 0..365
                var a = new Date(f.Y(), f.n() - 1, f.j()),
                    b = new Date(f.Y(), 0, 1);
                return Math.round((a - b) / 864e5);
            },

            // Week
            W: function() { // ISO-8601 week number
                var a = new Date(f.Y(), f.n() - 1, f.j() - f.N() + 3),
                    b = new Date(a.getFullYear(), 0, 4);
                return _pad(1 + Math.round((a - b) / 864e5 / 7), 2);
            },

            // Month
            F: function() { // Full month name; January...December
                return txt_words[6 + f.n()];
            },
            m: function() { // Month w/leading 0; 01...12
                return _pad(f.n(), 2);
            },
            M: function() { // Shorthand month name; Jan...Dec
                return f.F().slice(0, 3);
            },
            n: function() { // Month; 1...12
                return jsdate.getMonth() + 1;
            },
            t: function() { // Days in month; 28...31
                return (new Date(f.Y(), f.n(), 0)).getDate();
            },

            // Year
            L: function() { // Is leap year?; 0 or 1
                var j = f.Y();
                return j % 4 === 0 & j % 100 !== 0 | j % 400 === 0;
            },
            o: function() { // ISO-8601 year
                var n = f.n(),
                    W = f.W(),
                    Y = f.Y();
                return Y + (n === 12 && W < 9 ? 1 : n === 1 && W > 9 ? -1 : 0);
            },
            Y: function() { // Full year; e.g. 1980...2010
                return jsdate.getFullYear();
            },
            y: function() { // Last two digits of year; 00...99
                return f.Y().toString().slice(-2);
            },

            // Time
            a: function() { // am or pm
                return jsdate.getHours() > 11 ? "pm" : "am";
            },
            A: function() { // AM or PM
                return f.a().toUpperCase();
            },
            B: function() { // Swatch Internet time; 000..999
                var H = jsdate.getUTCHours() * 36e2,
                    // Hours
                    i = jsdate.getUTCMinutes() * 60,
                    // Minutes
                    s = jsdate.getUTCSeconds(); // Seconds
                return _pad(Math.floor((H + i + s + 36e2) / 86.4) % 1e3, 3);
            },
            g: function() { // 12-Hours; 1..12
                return f.G() % 12 || 12;
            },
            G: function() { // 24-Hours; 0..23
                return jsdate.getHours();
            },
            h: function() { // 12-Hours w/leading 0; 01..12
                return _pad(f.g(), 2);
            },
            H: function() { // 24-Hours w/leading 0; 00..23
                return _pad(f.G(), 2);
            },
            i: function() { // Minutes w/leading 0; 00..59
                return _pad(jsdate.getMinutes(), 2);
            },
            s: function() { // Seconds w/leading 0; 00..59
                return _pad(jsdate.getSeconds(), 2);
            },
            u: function() { // Microseconds; 000000-999000
                return _pad(jsdate.getMilliseconds() * 1000, 6);
            },

            // Timezone
            e: function() { // Timezone identifier; e.g. Atlantic/Azores, ...
                // The following works, but requires inclusion of the very large
                // timezone_abbreviations_list() function.
                /*              return that.date_default_timezone_get();
                 */
                throw 'Not supported (see source code of date() for timezone on how to add support)';
            },
            I: function() { // DST observed?; 0 or 1
                // Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC.
                // If they are not equal, then DST is observed.
                var a = new Date(f.Y(), 0),
                    // Jan 1
                    c = Date.UTC(f.Y(), 0),
                    // Jan 1 UTC
                    b = new Date(f.Y(), 6),
                    // Jul 1
                    d = Date.UTC(f.Y(), 6); // Jul 1 UTC
                return ((a - c) !== (b - d)) ? 1 : 0;
            },
            O: function() { // Difference to GMT in hour format; e.g. +0200
                var tzo = jsdate.getTimezoneOffset(),
                    a = Math.abs(tzo);
                return (tzo > 0 ? "-" : "+") + _pad(Math.floor(a / 60) * 100 + a % 60, 4);
            },
            P: function() { // Difference to GMT w/colon; e.g. +02:00
                var O = f.O();
                return (O.substr(0, 3) + ":" + O.substr(3, 2));
            },
            T: function() {
                return 'UTC';
            },
            Z: function() { // Timezone offset in seconds (-43200...50400)
                return -jsdate.getTimezoneOffset() * 60;
            },

            // Full Date/Time
            c: function() { // ISO-8601 date.
                return 'Y-m-d\\TH:i:sP'.replace(formatChr, formatChrCb);
            },
            r: function() { // RFC 2822
                return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb);
            },
            U: function() { // Seconds since UNIX epoch
                return jsdate / 1000 | 0;
            }
        };
        this.date = function(format, timestamp) {
            that = this;
            jsdate = (timestamp === undefined ? new Date() : // Not provided
                (timestamp instanceof Date) ? new Date(timestamp) : // JS Date()
                new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int)
            );
            return format.replace(formatChr, formatChrCb);
        };
        return this.date(format, timestamp);
    },
    in_array: function(needle, haystack, argStrict) {
        var key = '',
            strict = !!argStrict;
        if (strict) {
            for (key in haystack) {
                if (haystack[key] === needle) {
                    return true;
                }
            }
        } else {
            for (key in haystack) {
                if (haystack[key] == needle) {
                    return true;
                }
            }
        }
        return false;
    },
    array_merge: function() {
        var args = Array.prototype.slice.call(arguments),
            argl = args.length,
            arg,
            retObj = {},
            k = '',
            argil = 0,
            j = 0,
            i = 0,
            ct = 0,
            toStr = Object.prototype.toString,
            retArr = true;

        for (i = 0; i < argl; i++) {
            if (toStr.call(args[i]) !== '[object Array]') {
                retArr = false;
                break;
            }
        }

        if (retArr) {
            retArr = [];
            for (i = 0; i < argl; i++) {
                retArr = retArr.concat(args[i]);
            }
            return retArr;
        }

        for (i = 0, ct = 0; i < argl; i++) {
            arg = args[i];
            if (toStr.call(arg) === '[object Array]') {
                for (j = 0, argil = arg.length; j < argil; j++) {
                    retObj[ct++] = arg[j];
                }
            } else {
                for (k in arg) {
                    if (arg.hasOwnProperty(k)) {
                        if (parseInt(k, 10) + '' === k) {
                            retObj[ct++] = arg[k];
                        } else {
                            retObj[k] = arg[k];
                        }
                    }
                }
            }
        }
        return retObj;
    },
    //参考jquery.cookie
    cookie: function(key, value, options) {
        var pluses = /\+/g;
        var config = {};
        config.defaults = {};

        function raw(s) {
            return s;
        }

        function decoded(s) {
            return decodeURIComponent(s.replace(pluses, ' '));
        }

        // write
        if (value !== undefined) {
            options = $.extend({}, config.defaults, options);

            if (value === null) {
                options.expires = -1;
            }

            if (typeof options.expires === 'number') {
                var days = options.expires,
                    t = options.expires = new Date();
                t.setDate(t.getDate() + days);
            }

            value = config.json ? JSON.stringify(value) : String(value);

            return (document.cookie = [
                encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value),
                options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
                options.path ? '; path=' + options.path : '',
                options.domain ? '; domain=' + options.domain : '',
                options.secure ? '; secure' : ''
            ].join(''));
        }

        // read
        var decode = config.raw ? raw : decoded;
        var cookies = document.cookie.split('; ');
        for (var i = 0, l = cookies.length; i < l; i++) {
            var parts = cookies[i].split('=');
            if (decode(parts.shift()) === key) {
                var cookie = decode(parts.join('='));
                return config.json ? JSON.parse(cookie) : cookie;
            }
        }

        return null;
    },
    empty: function(mixed_var) {
        var undef, key, i, len;
        var emptyValues = [undef, null, false, 0, "", "0"];

        for (i = 0, len = emptyValues.length; i < len; i++) {
            if (mixed_var === emptyValues[i]) {
                return true;
            }
        }

        if (typeof mixed_var === "object") {
            for (key in mixed_var) {
                // TODO: should we check for own properties only?
                //if (mixed_var.hasOwnProperty(key)) {
                return false;
                //}
            }
            return true;
        }

        return false;
    },
    explode: function(delimiter, string, limit) {

        if (arguments.length < 2 || typeof delimiter === 'undefined' || typeof string === 'undefined') return null;
        if (delimiter === '' || delimiter === false || delimiter === null) return false;
        if (typeof delimiter === 'function' || typeof delimiter === 'object' || typeof string === 'function' || typeof string === 'object') {
            return {
                0: ''
            };
        }
        if (delimiter === true) delimiter = '1';

        // Here we go...
        delimiter += '';
        string += '';

        var s = string.split(delimiter);


        if (typeof limit === 'undefined') return s;

        // Support for limit
        if (limit === 0) limit = 1;

        // Positive limit
        if (limit > 0) {
            if (limit >= s.length) return s;
            return s.slice(0, limit - 1).concat([s.slice(limit - 1).join(delimiter)]);
        }

        // Negative limit
        if (-limit >= s.length) return [];

        s.splice(s.length + limit);
        return s;
    },
    implode: function(glue, pieces) {
        var i = '',
            retVal = '',
            tGlue = '';
        if (arguments.length === 1) {
            pieces = glue;
            glue = '';
        }
        if (typeof pieces === 'object') {
            if (Object.prototype.toString.call(pieces) === '[object Array]') {
                return pieces.join(glue);
            }
            for (i in pieces) {
                retVal += tGlue + pieces[i];
                tGlue = glue;
            }
            return retVal;
        }
        return pieces;
    },
    json_encode: function(mixed_val) {
        return JSON.stringify(mixed_val);
        var retVal, json = window.JSON;
        try {
            if (typeof json === 'object' && typeof json.stringify === 'function') {
                retVal = json.stringify(mixed_val); // Errors will not be caught here if our own equivalent to resource
                //  (an instance of PHPJS_Resource) is used
                if (retVal === undefined) {
                    throw new SyntaxError('json_encode');
                }
                return retVal;
            }

            var value = mixed_val;

            var quote = function(string) {
                var escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
                var meta = { // table of character substitutions
                    '\b': '\\b',
                    '\t': '\\t',
                    '\n': '\\n',
                    '\f': '\\f',
                    '\r': '\\r',
                    '"': '\\"',
                    '\\': '\\\\'
                };

                escapable.lastIndex = 0;
                return escapable.test(string) ? '"' + string.replace(escapable, function(a) {
                    var c = meta[a];
                    return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                }) + '"' : '"' + string + '"';
            };

            var str = function(key, holder) {
                var gap = '';
                var indent = '    ';
                var i = 0; // The loop counter.
                var k = ''; // The member key.
                var v = ''; // The member value.
                var length = 0;
                var mind = gap;
                var partial = [];
                var value = holder[key];

                // If the value has a toJSON method, call it to obtain a replacement value.
                if (value && typeof value === 'object' && typeof value.toJSON === 'function') {
                    value = value.toJSON(key);
                }

                // What happens next depends on the value's type.
                switch (typeof value) {
                    case 'string':
                        return quote(value);

                    case 'number':

                        return isFinite(value) ? String(value) : 'null';

                    case 'boolean':
                    case 'null':

                        return String(value);

                    case 'object':

                        if (!value) {
                            return 'null';
                        }
                        if ((this.PHPJS_Resource && value instanceof this.PHPJS_Resource) || (window.PHPJS_Resource && value instanceof window.PHPJS_Resource)) {
                            throw new SyntaxError('json_encode');
                        }

                        gap += indent;
                        partial = [];

                        // Is the value an array?
                        if (Object.prototype.toString.apply(value) === '[object Array]') {

                            length = value.length;
                            for (i = 0; i < length; i += 1) {
                                partial[i] = str(i, value) || 'null';
                            }

                            v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']';
                            gap = mind;
                            return v;
                        }

                        // Iterate through all of the keys in the object.
                        for (k in value) {
                            if (Object.hasOwnProperty.call(value, k)) {
                                v = str(k, value);
                                if (v) {
                                    partial.push(quote(k) + (gap ? ': ' : ':') + v);
                                }
                            }
                        }

                        v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}';
                        gap = mind;
                        return v;
                    case 'undefined':
                        // Fall-through
                    case 'function':
                        // Fall-through
                    default:
                        throw new SyntaxError('json_encode');
                }
            };

            return str('', {
                '': value
            });

        } catch (err) { // Todo: ensure error handling above throws a SyntaxError in all cases where it could
            // (i.e., when the JSON global is not available and there is an error)
            if (!(err instanceof SyntaxError)) {
                throw new Error('Unexpected error type in json_encode()');
            }
            this.php_js = this.php_js || {};
            this.php_js.last_error_json = 4; // usable by json_last_error()
            return null;
        }
    },
    json_decode: function(str_json) {
        var json = /*window.*/ JSON;
        if (typeof json === 'object' && typeof json.parse === 'function') {
            try {
                return json.parse(str_json);
            } catch (err) {
                if (!(err instanceof SyntaxError)) {
                    throw new Error('Unexpected error type in json_decode()');
                }
                this.php_js = this.php_js || {};
                this.php_js.last_error_json = 4; // usable by json_last_error()
                return null;
            }
        }

        var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
        var j;
        var text = str_json;


        cx.lastIndex = 0;
        if (cx.test(text)) {
            text = text.replace(cx, function(a) {
                return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            });
        }

        if ((/^[\],:{}\s]*$/).test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {


            j = eval('(' + text + ')');

            return j;
        }

        this.php_js = this.php_js || {};
        this.php_js.last_error_json = 4; // usable by json_last_error()
        return null;
    },
    intval: function(mixed_var, base) {
        var tmp;
        var type = typeof mixed_var;
        if (type === 'boolean') {
            return +mixed_var;
        } else if (type === 'string') {
            tmp = parseInt(mixed_var, base || 10);
            return (isNaN(tmp) || !isFinite(tmp)) ? 0 : tmp;
        } else if (type === 'number' && isFinite(mixed_var)) {
            return mixed_var | 0;
        } else {
            return 0;
        }
    },
    time: function() {
        return Math.floor(new Date().getTime() / 1000);
    },
    parse_hash: function(hash) {
        var result = {},
            res;
        if (this.empty(hash)) hash = window.location.hash;
        hash = this.trim(hash);
        if (hash.indexOf('#') === 0) {
            res = hash.split('#')[1].split('&');
        } else {
            res = hash.split('&');
        }
        for (i = 0; i < res.length; i++) {
            var obj = res[i].split('=');

            if (this.trim(obj[0]).length > 0) {
                result[obj[0]] = obj[1];
            }
        }
        return result;
    }
};
module.exports = mixin;